#include <arch.h>
#include <config.h>
#include <i2c.h>
#include <util.h>

enum{
    musicByMono = 1,
    musicByStero = 2,
    musicByHeadset = 3,
    musicPcmMono = 4,
};

void Dump_I2SRegs()
{
    printk("Dump I2S regs:\n");
    printk("IISCON  = 0x%x\n",  rIISCON );
    printk("IISMOD  = 0x%x\n",  rIISMOD );
    printk("IISPSR  = 0x%x\n",  rIISPSR );
    printk("IISFCON = 0x%x\n",  rIISFCON);
}

#define CODEC_I2CADDR 0x34
int Codec_Write(unsigned short reg, unsigned short value)
{
    unsigned char buffer[3];
    buffer[0] = (unsigned char)reg;
    buffer[1] = (unsigned char)(value >> 8);
    buffer[2] = (unsigned char)value;
    return I2cWriteBuffer(CODEC_I2CADDR, buffer, 3);
}

int Codec_Read(unsigned short reg, unsigned short *pValue)
{
    return I2cReadWord(CODEC_I2CADDR | 0x1, reg, pValue);
}

int Codec_SetSampleRate(int rate)
{
    switch (rate) {
        case 8000:
        case 44100:
            // TO DO
            // clkrate = 16934400;
            break;

        default:
            printk("Not supported now.\n");
            return -1;
    }

    if (rate == 8000) {
        Codec_Write(7, 0x50d8);      // Clocking 1
        Codec_Write(6, 0x01d8);      // Clocking 2
        Codec_Write(8, 0x8840);      // Audio Interface 3
        Codec_Write(9, 0x8840);      // Audio Interface 4
        Codec_Write(60, 0x00cb);     // PLL1
        Codec_Write(61, 0x009c);     // PLL2
        Codec_Write(62, 0x0027);     // PLL3
        ostdelay(1);
    }
    else {
        Codec_Write(7, 0x0024);
        Codec_Write(8, 0x0040);
        Codec_Write(9, 0x8040);
        ostdelay(1);
    }

    return 0;

}

void Codec_ReplayBySpeaker()
{
    Codec_Write(45, 0x0001); // Left DAC to LOMIX
    Codec_Write(46, 0x0001); // Right DAC to ROMIX
    Codec_Write(4 , 0x0010);
    Codec_Write(10, 0x0000);
    Codec_Write(31, 0x0022);
    Codec_Write(32, 0x017f);
    Codec_Write(33, 0x017f);
    Codec_Write(34, 0x0000);
    Codec_Write(35, 0x0103);
    Codec_Write(51, 0x0180);
    Codec_Write(54, 0x000c);
    Codec_Write(2,  0x6000);
    Codec_Write(3 , 0x00f3);
    Codec_Write(1 , 0x1013);
}

void Codec_ReplayByHeadset()
{
    Codec_Write(31, 0x0022);
    Codec_Write(34, 0x0003);
    Codec_Write(35, 0x0103);
    Codec_Write(28, 0x0163);
    Codec_Write(29, 0x0163);
    Codec_Write(4,  0x0010);
    Codec_Write(10, 0x0000);
    Codec_Write(45, 0x0001);
    Codec_Write(46, 0x0001);
    Codec_Write(54, 0x0000);
    Codec_Write(2,  0x6000);
    Codec_Write(3,  0x0033);
    Codec_Write(1,  0x0313);
}

void Codec_RecordByMainMIC()
{
    Codec_Write(4,  0x0010);
    Codec_Write(24, 0x0117);
    Codec_Write(40, 0x0030);
    Codec_Write(41, 0x0030);
    Codec_Write(42, 0x0000);
    Codec_Write(2,  0x6242);
    Codec_Write(1,  0x0013);
}

void Codec_RecordByAuxMIC()
{
    Codec_Write(4,  0xc010);
    Codec_Write(26, 0x0117);
    Codec_Write(40, 0x0003);
    Codec_Write(41, 0x0000);
    Codec_Write(42, 0x0030);
    Codec_Write(2,  0x6111);
    Codec_Write(1,  0x0013);
}

void Codec_CallByReceiver()
{
    Codec_Write(35, 0x0103);
    Codec_Write(4,  0x0010);
    Codec_Write(10, 0x0200);
    Codec_Write(24, 0x010c);
    Codec_Write(25, 0x010b);
    Codec_Write(30, 0x0006);
    Codec_Write(31, 0x0000);
    Codec_Write(32, 0x017b);
    Codec_Write(34, 0x0003);
    Codec_Write(40, 0x00f0);
    Codec_Write(41, 0x0100);
    Codec_Write(45, 0x0041);
    Codec_Write(46, 0x0001);
    Codec_Write(51, 0x0191);
    Codec_Write(52, 0x0012);
    Codec_Write(54, 0x0000);
    Codec_Write(2,  0x62c0);
    Codec_Write(3,  0x30a2);
    Codec_Write(1,  0x0c13);
}

int I2S_SetSampleRate(int rate)
{
    switch (rate) {
        case 8000:
            rIISMOD |= 1 << 8; // Slave mode
        break;

        default:
            rIISMOD &= ~(1 << 8); // Master mode
        break;
    }

    return 0;
}

void I2S_InitReplay()
{
    // Transmit FIFO access: DMA (1 << 15)
    // Transmit FIFO: Enable (1 << 13)
    rIISFCON = (1 << 15) | (1 << 13);
    // Transmit DMA service request enable (1 << 5)
    // IIS interface enable (1 << 0)
    rIISCON = (1 << 5) | (1 << 0);
    // Master clock select: MPLLin (1 << 9)
    // Transmit mode  (2 << 6)
    // Serial data bit per channel: 16bit (1 << 3)
    // Master clock frequency select: 384fs (1 << 2)
    // Serial bit clock frequency select: 32fs (1 << 0)
    rIISMOD = (1 << 9) | (2 << 6) | (1 << 3) | (1 << 2) | (1 << 0);
}

void I2S_AfterReplay()
{
    rIISCON    &= ~(1<<0); //IIS Interface stop
    rIISFCON = 0x0;        //For FIFO flush
}

void I2S_InitRecord()
{
    // Receive FIFO access: DMA (1 << 14)
    // Receive FIFO: Enable (1 << 12)
    rIISFCON = (1 << 14) | (1 << 12);
    // Receive DMA service request enable (1 << 4)
    // IIS interface enable (1 << 0)
    rIISCON = (1 << 4) | (1 << 0);
    // Master clock select: MPLLin (1 << 9)
    // Transmit mode  (1 << 6)
    // Serial data bit per channel: 16bit (1 << 3)
    // Master clock frequency select: 384fs (1 << 2)
    // Serial bit clock frequency select: 32fs (1 << 0)
    rIISMOD = (1 << 9) | (1 << 6) | (1 << 3) | (1 << 2) | (1 << 0);
}

void I2S_AfterRecord()
{
    rIISCON    &= ~(1<<0); //IIS Interface stop
    rIISFCON = 0x0;        //For FIFO flush
}

void DMA_InitReplayTransfer(unsigned long buffer, int len)
{
    //DMA2 Register Setting
    rDISRC2 = buffer;
    rDISRCC2 = (0<<1) | (0<<0); // Source: AHB, Increment
    rDIDST2  = (unsigned long)IISFIFO;
    rDIDSTC2 = (0<<2) | (1<<1) | (1<<0); // Destination: APB, Fixed
    // Disable interrupt (bit29)
    // Datasize: Halfword (bit21~bit20)
    rDCON2   = (1<<31)|(0<<30)|(0<<29)|(0<<28)|(0<<27)|(0<<24)|
               (1<<23)|(0<<22)|(1<<20)|(len/2);
    rDMASKTRIG2 = (0<<2) | (1<<1) | (0<<0);      //No-stop, DMA2 channel On, and No-sw trigger

}

void DMA_WaitForReplayExit()
{
    while ((rDSTAT2 & 0xfffff) > 0) {
    }

    rDMASKTRIG2  = (1<<2);     //DMA2 stop
    printk("DMA transmission is finished!\n");
}

void DMA_InitRecordTransfer(unsigned long buffer, int len)
{
    //DMA1 Register Setting
    rDISRC1 = (unsigned long)IISFIFO;
    rDISRCC1 = (1<<1) | (1<<0); // Source: APB, Fixed
    rDIDST1  = buffer;
    rDIDSTC1 = (0<<2) | (0<<1) | (0<<0); // Destination: AHB, Increment
    // Disable interrupt (bit29)
    // Datasize: Halfword (bit21~bit20)
    rDCON1   = (1<<31)|(0<<30)|(0<<29)|(0<<28)|(0<<27)|(2<<24)|
               (1<<23)|(1<<22)|(1<<20)|(len/2);
    rDMASKTRIG1 = (0<<2) | (1<<1) | (0<<0);      //No-stop, DMA1 channel On, and No-sw trigger
}

void DMA_WaitForRecordExit()
{
    while ((rDSTAT1 & 0xfffff) > 0) {
    }

    rDMASKTRIG1  = (1<<2);     //DMA2 stop
    printk("DMA transmission is finished!\n");
}

int Audio_SetSampleRate(int rate)
{
    I2S_SetSampleRate(rate);
    return Codec_SetSampleRate(rate);
}

void Audio_Init(int param)
{
    unsigned char reg;
    unsigned short codecID;

    InitI2c();

    rCLKCON |= 1 << 17; // Enable IIS clock
    ostdelay(10);

    // Enable LDO for audio codec
    I2cReadByte(0x92, 0x93, &reg);
    reg &= 0xf0; // LDO6, 1.8V
    I2cWriteByte(0x92, 0x93, reg);

    I2cReadByte(0x92, 0x94, &reg);
    reg &= 0xf0; // LDO8, 2.8V
    I2cWriteByte(0x92, 0x94, reg);

    I2cReadByte(0x92, 0x97, &reg);
    reg |= 1 << 6; // LDO6
    I2cWriteByte(0x92, 0x97, reg);

    I2cReadByte(0x92, 0x98, &reg);
    reg |= 1 << 0; // LDO8
    I2cWriteByte(0x92, 0x98, reg);

    codecID = 0;
    Codec_Read(0x0, &codecID);
    printk("Codec ID: 0x%x\n", codecID);

    return;
}

int Audio_SetOutPath(int path)
{
    switch (path) {
    case 0:
        Codec_ReplayBySpeaker();
        break;

    case 1:
    default:
        Codec_ReplayByHeadset();
        break;
    }

    return 0;
}

int Audio_SetInPath(int path)
{
    switch (path) {
    case 0:
        Codec_RecordByMainMIC();
        break;

    case 1:
    default:
        Codec_RecordByAuxMIC();
        break;
    }

    return 0;
}

int Audio_PrepareReplay(int rate)
{
    I2S_InitReplay();

    // Set default sample rate
    Audio_SetSampleRate(rate);

    Codec_Write(51, 0x0191);
    Codec_Write(31, 0x0022); // OUT3 & OUT4 Mute
    Codec_Write(3,  0x00f3); // Left & Right DAC enable
    Codec_Write(1,  0x0c13); // MICBIAS & Vref enable

    return 0;
}

int Audio_PrepareRecord(int rate)
{
    I2S_InitRecord();

    // Set default sample rate
    Audio_SetSampleRate(rate);

    Codec_Write(51, 0x0191);
    Codec_Write(31, 0x0022); // OUT3 & OUT4 Mute
    Codec_Write(3,  0x00f3); // Left & Right DAC enable
    Codec_Write(1,  0x0c13); // MICBIAS & Vref enable

    return 0;
}

// pBuffer: 16bit stereo audio data
int Audio_Play(void *pBuffer, int len)
{
    memcpy((void *)AUDIO_DMABUFFER, pBuffer, len);
    DMA_InitReplayTransfer(AUDIO_DMABUFFER, len);

    // Waiting
    DMA_WaitForReplayExit();

    I2S_AfterReplay();

    return 0;
}

// pBuffer: 16bit stereo audio data
int Audio_Record(void *pBuffer, int len)
{
    DMA_InitRecordTransfer(AUDIO_DMABUFFER, len);

    // Waiting
    DMA_WaitForRecordExit();

    I2S_AfterRecord();

    memcpy(pBuffer, (void *)AUDIO_DMABUFFER, len);

    return 0;
}

int Codec_SetVolume(unsigned char left, unsigned char right, unsigned int volType)
{

    return 0;
}

void Audio_Close(void)
{
    return;
}

